package com.demo.webapp.security.method.access.intercept;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.annotation.AnnotationMetadataExtractor;
import org.springframework.security.access.method.AbstractFallbackMethodSecurityMetadataSource;
import org.springframework.util.Assert;

import com.demo.core.context.Refreshable;
import com.demo.core.model.Site;
import com.demo.core.model.SiteFunction;
import com.demo.core.service.base.SiteFunctionService;
import com.demo.core.service.base.SiteService;
import com.demo.security.annotation.function.Function;
import com.demo.webapp.context.MultisiteContextHolder;

/**
 * 基于多站点访问的持久化XML方法安全元数据资源类 提供方法-安全配置属性对应Map 
 * <br>该类提供:
 * {@link MethodSecurityMetadataSource} 实现。 该类参照
 * {@link org.springframework.security.access.method.MapBasedMethodSecurityMetadataSource}
 * 提供更细粒度的安全方法
 * 
 * @author ryuu.kk
 * 
 */
@SuppressWarnings({"unchecked"})
public class MultisitePersistentMethodSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource implements InitializingBean, Refreshable {
	
	// 默认匿名角色名
	private static final String ROLE_ANONYMOUS = "ROLE_ANONYMOUS";
	
	// ~ Instance fields
	/**
	 * 刷新次数
	 */
	private int refreshCount = 0;
	
	/**
	 * 最后刷新时间
	 */
	private long lastRefreshTime;
	
	/**
	 * 站点服务
	 */
	private SiteService siteService;
	
	/**
	 * 站点功能
	 */
	private SiteFunctionService siteFunctionService;
	
	/**
	 * 匿名角色名
	 */
	private String anonymousRole = ROLE_ANONYMOUS;
	
	/**
	 * 注解类型
	 */
	private Class<? extends Annotation> annotationType;
	
	/**
	 * 注解提取器
	 */
	private FunctionAnnotationMetadataExtractor annotationExtractor;
	
	/**
	 * 多站点的方法元数据安全资源
	 */
	private Map<String, Map<Long, SiteFunction>> multisiteSerucityMetadataMap;
	
    public MultisitePersistentMethodSecurityMetadataSource() {
    	this.annotationExtractor = new FunctionAnnotationMetadataExtractor(this.anonymousRole);
    	//init(annotationExtractor);
    }

	public MultisitePersistentMethodSecurityMetadataSource(FunctionAnnotationMetadataExtractor annotationMetadataExtractor) {
		this.annotationExtractor = annotationMetadataExtractor;
		// 初始化
		//init(annotationMetadataExtractor);
	}
	
	/**
	 * 初始化函数
	 * @param annotationMetadataExtractor FunctionAnnotationMetadataExtractor
	 */
	private void init(FunctionAnnotationMetadataExtractor annotationMetadataExtractor) {
		
		if (this.multisiteSerucityMetadataMap == null) {
			this.multisiteSerucityMetadataMap = new HashMap<String, Map<Long, SiteFunction>>();
		}
		Assert.notNull(annotationMetadataExtractor);
		annotationExtractor = annotationMetadataExtractor;
		annotationType = (Class<? extends Annotation>) GenericTypeResolver
				.resolveTypeArgument(annotationExtractor.getClass(),
						AnnotationMetadataExtractor.class);
		Assert.notNull(
				annotationType,
				annotationExtractor.getClass().getName()
						+ " must supply a generic parameter for AnnotationMetadataExtractor");
		// 站点KEY
		List<Site> siteList = siteService.findAll();
		for (Site site : siteList) {
			Long siteId = site.getId();
			Map<Long, SiteFunction> siteFunctionMap = siteFunctionService.querySiteFunctionAndRoleMap(siteId, com.demo.core.model.Function.METHOD);
			multisiteSerucityMetadataMap.put(site.getDomain(), siteFunctionMap);
		}
	}
	
	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		return null;
	}

	@Override
	protected Collection<ConfigAttribute> findAttributes(Method method,	Class<?> targetClass) {
		return processAnnotation(AnnotationUtils.findAnnotation(method, annotationType));
	}

	/**
	 * 取得目标类的所有安全属性数据
	 * <br>
	 * <br>这里需要注意的是,如果对象上持有@Function注解,则创建该对象并调用任何方法(一般Spring初始化的setXXX方法)时,
	 * <br>都将触发投票动作。如果@Function注解被配置到Action上,则Action创建后的初始化则会被进行投票。
	 * <br>如果只对指定方法进行投票而非类的所有方法,则findAttributes(Class<?> clazz)方法返回null即可。
	 * <br>如果要对该对象的所有方法进行权限判断,则findAttributes(Class<?> clazz)返回指定安全属性数据Collection<ConfigAttribute> 
	 */
	@Override
	protected Collection<ConfigAttribute> findAttributes(Class<?> clazz) {
		//return processAnnotation(AnnotationUtils.findAnnotation(clazz, annotationType));
		return null;
	}
	
    private Collection<ConfigAttribute> processAnnotation(Annotation a) {
        if (a == null) {
            return null;
        }
        if (a instanceof Function) {
        	return annotationExtractor.extractAttributes((Function)a);
        }
        return null;
    }
    
	public void setSiteService(SiteService siteService) {
		this.siteService = siteService;
	}

	public void setSiteFunctionService(SiteFunctionService siteFunctionService) {
		this.siteFunctionService = siteFunctionService;
	}
	
	class FunctionAnnotationMetadataExtractor implements AnnotationMetadataExtractor<Function> {
		private String anonymousRole = ROLE_ANONYMOUS;
		private FunctionAnnotationMetadataExtractor(String anonymousRole) {
			this.anonymousRole = anonymousRole;
		}
	    public Collection<ConfigAttribute> extractAttributes(Function secured) {
	    	List<ConfigAttribute> attributes = null;
	    	Long id = (long) secured.id();
			String key = "";
			// 得到站点
			Site site = MultisiteContextHolder.getSite();
			key = site.getDomain();
			
	    	Map<Long, SiteFunction> siteFucntionMap = multisiteSerucityMetadataMap.get(key);
	    	SiteFunction sf = siteFucntionMap.get(id);
	    	if (sf == null) {
	    		logger.warn("功能ID:" + id + "不存在功能列表(T_CORE_FUNCTION)中. 该功能不做安全判断");
	    		return null;
	    	}
	    	com.demo.core.model.Function func = sf.getFunction();
	    	
	    	attributes = new ArrayList<ConfigAttribute>();
	    	
	    	if (func != null && func.getIsAnonymous()) {
	    		attributes.add(new SecurityConfig(this.anonymousRole));
	    	}
	    	List<String> rolelist = sf.getRoleList();
	    	
	    	if (rolelist != null) {
		        for(String token : rolelist) {
		            attributes.add(new SecurityConfig(token));
		        }
	    	}
	        return attributes;
	    }
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		init(this.annotationExtractor);
	}


	@Override
	public void refresh() {
		
		synchronized(multisiteSerucityMetadataMap) {
			if (multisiteSerucityMetadataMap != null) {
				multisiteSerucityMetadataMap.clear();
			}
			// 重新初始化对象
			init(this.annotationExtractor);
		}
		
		++ refreshCount;
	}

	@Override
	public long getRefreshCount() {
		return refreshCount;
	}

	@Override
	public long getLastRefreshTime() {
		return lastRefreshTime;
	}
	
	public void setLastRefreshTime(long lastRefreshTime) {
		this.lastRefreshTime = lastRefreshTime;
	}

	public Map<String, Map<Long, SiteFunction>> getMultisiteSerucityMetadataMap() {
		return multisiteSerucityMetadataMap;
	}
	
	public void setAnonymousRole(String anonymousRole) {
		this.anonymousRole = anonymousRole;
	}
}
